/*
 * Copyright (C) 2010-2011 Comcast Cable.  All Rights Reserved.
 */
package com.hercules.android.receiver.connection.parser.json.itemparsers;

import com.google.gjson.JSONArray;
import com.google.gjson.JSONException;
import com.google.gjson.JSONObject;
import com.hercules.android.receiver.connection.parser.CommandParsingException;
import com.hercules.android.receiver.connection.parser.MalformedCommandException;
import com.hercules.android.receiver.connection.parser.json.ItemParser;

import java.util.ArrayList;
import java.util.List;

/**
 * Parser for List properties.
 * <p>
 * AndroidAssembly2 this class was added in Android Assembly 2
 *
 * @author 7realm
 * @version 1.0
 */
public class ListParser extends DependentParser {
    /**
     * Constructor for {@link ListParser}.
     *
     * @param required if property is required
     * @param dependent the name of dependent property
     */
    public ListParser(boolean required, String dependent) {
        super(required, dependent);
    }


    /**
     * Parses list property.
     *
     * @param path the path to value
     * @param json the JSON
     * @param keyName the name of current JSON key
     * @param instance the instance of object, parsed value will be set to it
     * @return the parsed value
     * @throws CommandParsingException if error occurs while parsing
     */
    @SuppressWarnings("boxing")
    @Override
    protected Object doParse(StringBuilder path, JSONObject json, String keyName, Object instance)
        throws CommandParsingException {
        super.doParse(path, json, keyName, instance);

        try {
            // get array
            JSONArray array = json.get(keyName).isArray();

            if (array == null || array.isNull() != null) {
            	return null;
            }
            
            List<Object> result = new ArrayList<Object>();
            
            // get sub map
            String prefix = path.toString();

            // check if we have case of list with any number of same items
            if (PARSERS.containsKey(prefix + ".*")) {
                ItemParser commonParser = PARSERS.get(prefix + ".*");
                for (int i = 0; i < array.size(); i++) {
                    commonParser.parse(join(path, "*"), array, i, result, SetterType.LIST);
                }
            } else {
                // we have strict number of supported items
                for (int i = 0; i < array.size(); i++) {
                    // get parser and parse
                    ItemParser parser = PARSERS.get(prefix + "." + i);
                    if (parser == null) {
                        warn(this, "Parser for {0}.{1} is missing. JSON {2}.", prefix, i, json);
                    } else {
                        parser.parse(join(path, Integer.toString(i)), array, i, result, SetterType.LIST);
                    }
                }

                // check if any of required values are left
                int i = array.size();
                while (true) {
                    ItemParser itemParser = PARSERS.get(prefix + "." + i);
                    if (itemParser == null) {
                        break;
                    }
                    if (itemParser.isRequired()) {
                        warn(this, "Missing value for required item {0}.{1}. JSON {2}.", prefix, i, json);
                    }
                    i++;
                }
            }
            return result;
        } catch (JSONException e) {
            throw new MalformedCommandException("Expected JSON array at '" + path + "' at JSON '" + json + "'.", e);
        }
    }
}
